#include "player.h"
#include "OgreParticleSystem.h"
#include <OgreEntity.h>
#include <OgreParticleEmitter.h>
#include "world.h"
#include "wii.h"
#include "weapon/wgun.h"
#include "weapon/wlaser.h"
#include "weapon/wstandard.h"
#include <strstream>
#include <iostream>

Player::Player(World *world)
{
    m_scene = world->getSceneManager();
    m_world = world;
    m_weapon = new WStandard(m_world, 10, 400);
    m_currentWeapon = 3;
    m_logger = Singleton::GetInstance();
    m_deflecting = false;

    // initialize physics variables
    slideAmount = 0.99;
    acceleration = 0.04;
    normSpeed = 10;
    maxSpeed = 10000;
    rollAmount = 0;
    shipVector = Ogre::Vector3(0,0,0);
    tLastShot = 0;
    setNpc(false);

    // define bounding box
    worldEdgeLeft = m_world->getWidth()/2;
    worldEdgeRight =  0 - m_world->getWidth()/2;
    worldEdgeTop = 3*m_world->getHeight() / 4;
    worldEdgeBottom = 0-m_world->getHeight() / 4;

    std::cout << worldEdgeLeft <<" "<< worldEdgeRight << " "<< worldEdgeTop << " " <<  worldEdgeBottom << std::endl;

    // create Shield and attach it to node
    Ogre::Entity* shield = m_scene->createEntity("sphere.mesh");
    shield->setMaterialName("grey");

    m_shieldNode = m_scene->getRootSceneNode()->createChildSceneNode();
    m_shieldNode->attachObject(shield);
    m_shieldNode->scale(0.1,0.1,0.1);

    // create player vessel and attach it to node
    Ogre::Entity* spaceShip = m_scene->createEntity("Model.mesh");

    m_node = m_scene->getRootSceneNode()->createChildSceneNode();
    m_node->attachObject(spaceShip);
    m_node->scale(0.6,0.6,0.6);
    m_health = 100;

    // create a particle system with 200 quota, then set its material and dimensions
    Ogre::ParticleSystem* thrusters = m_scene->createParticleSystem(25);
    thrusters->setMaterialName("Examples/Flare");
    thrusters->setDefaultDimensions(22, 22);
    //thrusters->setKeepParticlesInLocalSpace(true);

    // create two emitters for our thruster particle system
    for (unsigned int i = 0; i < 2; i++)
    {
        Ogre::ParticleEmitter* emitter = thrusters ->addEmitter("Point");  // add a point emitter

            // set the emitter properties
            emitter->setAngle(Ogre::Degree(3));
            emitter->setTimeToLive(0.04);
            emitter->setEmissionRate(1000);
            emitter->setParticleVelocity(300);
            emitter->setDirection(Ogre::Vector3::NEGATIVE_UNIT_Z);
            emitter->setColour(Ogre::ColourValue::White);
            emitter->setPosition(Ogre::Vector3(i == 0 ? 5.7 : -18, 0, 0));
    }

    // attach our thruster particles to the rear of the ship
    m_node->createChildSceneNode(Ogre::Vector3(0, 0, -255))->attachObject(thrusters);
}

/**
  * player can collide
  *
  */

bool Player::colides(){
    return true;
}

/**
  * remove projectile and drain life if player is hit
  *
  */

void Player::colide(AI *object){
    if((object->getType() == TYPE_BULLET || object->getType() == TYPE_FLAMER || object->getType() == TYPE_FLYER) && object->isNpc()){
        //m_health -= object->getDamage();

        //create particle effect
        this->animDeath(this->getPosition());

        //log info
        std::stringstream logInfo;
        logInfo << "score update, score value," << m_logger->m_score-- << ", player was hit by enemy vessel";
        m_logger->log(logInfo.str());
        m_world->setScore(m_logger->m_score);
        if(m_health <= 0){
            m_world->removeActor(this);
        }
    }
}

/**
  * movement methods
  *
  */

void Player::moveLeft(float time){
    shipVector.x += acceleration*(time*300);
    if(shipVector.x >= maxSpeed){
        shipVector.x = maxSpeed;
    }
    // set roll
    if(rollAmount > -40){
        m_node->roll(Ogre::Degree(-1));
        rollAmount--;
    }
}

void Player::moveRight(float time){
    shipVector.x -= acceleration*(time*300);
    if(shipVector.x <= -maxSpeed){
        shipVector.x = -maxSpeed;
    }
    //set roll
    if(rollAmount < 40){
        m_node->roll(Ogre::Degree(1));
        rollAmount++;
    }
}

void Player::moveUp(float time){
    shipVector.z += acceleration*(time*300);
    if(shipVector.z >= maxSpeed){
        shipVector.z = maxSpeed;
    }  
}

void Player::moveDown(float time){
    shipVector.z -= acceleration*(time*300);
    if(shipVector.z <= -maxSpeed){
        shipVector.z = -maxSpeed;
    }
}

/**
  * fire current weapon in regard to rate of fire
  *
  */

void Player::fire(){

    if(m_health > 0){
        int fireControll = m_world->getCurrentTime()-tLastShot;

        if(fireControll >= m_weapon->m_rof){
            m_weapon->reload();
            for(int i = 0; i < m_weapon->m_numOfProjectiles; i++){
                m_world->addActor(m_weapon->getProjectile(i),m_node->getPosition());
            }
            tLastShot = m_world->getCurrentTime();
        }
    }
}

/**
  * set weapon as passed through parameter
  *
  */

void Player::setWeapon(int activeWeapon){
    switch(activeWeapon){
    case 0:
        m_weapon = new wLaser(m_world, 6, 400);
        m_currentWeapon = STANDARD;
    case 1:
        m_weapon = new wLaser(m_world, 30, 400);
        m_currentWeapon = SPREADER;
        break;
    case 2:
        m_weapon = new WGun(m_world, 5, 100);
        m_currentWeapon = FLAMER;
        break;
    case 3:
        m_weapon = new WStandard(m_world, 2, 400);
        m_currentWeapon = LINER;
        break;
    case 4:
        //m_currentWeapon = LASER;
        m_currentWeapon = 4;
        break;
    case 5:
        //m_currentWeapon = BUBBLER;
        m_currentWeapon = 5;
        break;
    default:
        std::cout << "ERROR: invalid weapon selection!\n";
    }
}

/**
  * get next weapon
  *
  */

void Player::weaponNext(){

    setWeapon(Wii::mod(++m_currentWeapon, NUMWEAPONS));
}

/**
  * get previous weapon
  *
  */

void Player::weaponPrevious(){

    if(--m_currentWeapon<=0){
        m_currentWeapon = 5;
    }
    setWeapon(m_currentWeapon);
}

/**
  * adjust the rate of fire (up or down) within boundary
  *
  */

void Player::setROF(int newROF){
    switch(newROF){
    case 1:
        if(m_weapon->m_rof >= 50){
            m_weapon->m_rof -= 10;
        }
        break;
    case 2:
        if(m_weapon->m_rof <= 5000){
            m_weapon->m_rof += 10;
        }
        break;
    }
}

/**
  * adjust position and keep in bounds, called every frame
  *
  */

void Player::move(float time){

    //m_shieldNode->setPosition(this->getPosition());
    m_shieldNode->setPosition(this->getPosition());
    adjustPosition(time);
    bindToBox();
}

/**
  * set position of player and adjust roll
  *
  */

void Player::adjustPosition(float time){

    // normalize roll
    if(rollAmount < 0){
        m_node->roll(Ogre::Degree(0.3));
        rollAmount += 0.3;
    }
    if(rollAmount > 0){
        m_node->roll(Ogre::Degree(-0.3));
        rollAmount -= 0.3;
    }

    // decrease x/z vector component to simulate momentum
    shipVector.x *= slideAmount;
    shipVector.z *= slideAmount;

    // update ship vctor with new values
    m_node->setPosition(m_node->getPosition() + Ogre::Vector3(shipVector.x,0,shipVector.z)*(time*300));
}

/**
  * keep player within boundin box
  *
  */

void Player::bindToBox(){

    // get x and z position of player
    Ogre::Real shipPositionX = m_node->getPosition().x;
    Ogre::Real shipPositionZ = m_node->getPosition().z;

    // inverse appropriate vector component if ship leaves bounds, prevent sticking to box
    if(shipPositionX >= worldEdgeLeft-150 && shipVector.x >= 0){
         shipVector.x *= -0.8;
    }
    if(shipPositionX <= worldEdgeRight+150 && shipVector.x <= 0){
        shipVector.x *= -0.8;
    }
    if(shipPositionZ >= worldEdgeTop+32 && shipVector.z >= 0){
        shipVector.z *= -0.8;
    }
    if(shipPositionZ <= worldEdgeBottom+200 && shipVector.z <= 0){
        shipVector.z *= -0.8;
    }
}

/**
  * destroy player object
  *
  */

void Player::destroy(){
    m_node->getParent()->removeChild(m_node);
}

void Player::shield(bool onOf){
    if(onOf){
        m_shieldNode->setScale(3,3,3);
    }
    else{
        m_shieldNode->setScale(0.1,0.1,0.1);
    }
}

/**
  * create particle effect once player is dstroyed
  *
  */

void Player::animDeath(Ogre::Vector3 lastPos){

    Ogre::SceneManager *scene = m_world->getSceneManager();

    // create particle system and attach it to d_node at death position
    Ogre::ParticleSystem* splatter = scene->createParticleSystem(25);
    splatter->setMaterialName("Examples/Flare");
    splatter->setDefaultDimensions(60, 60);

    Ogre::ParticleEmitter* emitter = splatter ->addEmitter("Point");  // add a point emitter

    // set the emitter properties
    emitter->setAngle(Ogre::Degree(90));
    emitter->setMinTimeToLive(0.1);
    emitter->setMaxTimeToLive(0.5);
    emitter->setDuration(0.05);
    emitter->setEmissionRate(1000);
    emitter->setParticleVelocity(300);
    emitter->setDirection(Ogre::Vector3::NEGATIVE_UNIT_Z);
    emitter->setColour(Ogre::ColourValue::Red);

    Ogre::SceneNode *d_node = scene->getRootSceneNode()->createChildSceneNode();
    d_node->setPosition(lastPos);
    d_node->attachObject(splatter);
}
